package ee.ut.maxorato_macobo.oop.mud.game;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;

import ee.ut.maxorato_macobo.oop.mud.items.ContainsItems;
import ee.ut.maxorato_macobo.oop.mud.items.ItemBase;
import ee.ut.maxorato_macobo.oop.mud.items.ItemLoader;
import ee.ut.maxorato_macobo.oop.mud.items.ItemLoaderPool;
import ee.ut.maxorato_macobo.oop.mud.items.ItemTemplate;
import ee.ut.maxorato_macobo.oop.mud.items.ItemTemplatePool;

public class DataParser {
	public static boolean parseDataFile(String fileName) {
		File areaFile = new File(fileName);
		
		try {
			parseDataText(new Scanner(areaFile));
		}
		catch (FileNotFoundException e) {
			return false;
		}
		
		return true;
	}
	
	public static void parseDataText(Scanner parser) {
		while(parser.hasNextLine()) {
			String line = parser.nextLine().trim();
			
			if(line.equals("room")) {
				parseRoomInfo(parser);
			}
			else if(line.equals("itemt")) {
				parseItemTempInfo(parser);
			}
			else if(line.equals("itemi")) {
				parseItemInstInfo(parser, false);
			}
			else if(line.equals("npct")) {
				parseNpcTempInfo(parser);
			}
			else if(line.equals("npci")) {
				parseNpcInstInfo(parser);
			}
		}
	}
	
	public static GameRoom parseRoomInfo(Scanner parser) {
		String areaName = null, roomName = null, roomDesc = null, roomExits = null;
		int roomIndex = 0;
		
		while(parser.hasNextLine()) {
			String[] parts = parser.nextLine().trim().split(" ", 2);
			
			if(parts[0].equals("ident")) {
				areaName = GameArea.getAreaNameFromIdent(parts[1]);
				roomIndex = GameArea.getRoomIndexFromIdent(parts[1]);
			}
			else if(parts[0].equals("name")) {
				roomName = parts[1].trim();
			}
			else if(parts[0].equals("desc")) {
				if(roomDesc == null) roomDesc = parts[1];
				else roomDesc += "\r\n" + parts[1];
			}
			else if(parts[0].equals("exits")) {
				roomExits = parts[1].trim();
			}
			else if(parts[0].equals("sub")) {
				parseDummy(parser);
			}
			else if(parts[0].equals("end")) {
				break;
			}
		}
		
		if(areaName != null && roomName != null && roomDesc != null && roomExits != null && roomIndex != 0) {
			GameArea targetArea = GameArea.findAreaByName(areaName);
			if(targetArea == null) targetArea = GameArea.createArea(areaName);
			
			if(targetArea.findRoom(roomIndex) == null) {
				GameRoom newRoom = new GameRoom(targetArea, roomIndex, roomName, roomDesc, roomExits);
				
				System.out.println("Adding room");
				
				return newRoom;
			}
			else {
				System.err.println("Duplicate room " + areaName + "," + roomIndex);
			}
		}
		
		return null;
	}
	
	public static ItemTemplate parseItemTempInfo(Scanner parser) {
		String itemName = null, itemLongName = null, itemDesc = null, templateName = null, typeClass = "Item";
		boolean readExtraSection = false;
		
		while(parser.hasNextLine()) {
			String[] parts = parser.nextLine().trim().split(" ", 2);
			
			if(parts[0].equals("ident")) {
				templateName = parts[1].trim();
			}
			else if(parts[0].equals("class")) {
				typeClass = parts[1].trim();
			}
			else if(parts[0].equals("name")) {
				itemName = parts[1].trim();
			}
			else if(parts[0].equals("longname")) {
				itemLongName = parts[1].trim();
			}
			else if(parts[0].equals("desc")) {
				itemDesc = parts[1].trim();
			}
			else if(parts[0].equals("extra")) {
				readExtraSection = true;
				break;
			}
			else if(parts[0].equals("sub")) {
				parseDummy(parser);
			}
			else if(parts[0].equals("end")) {
				break;
			}
		}
		
		if(itemName != null && itemLongName != null && itemDesc != null && templateName != null) {
			try {
				String className = ItemTemplate.class.getName().substring(0, ItemTemplate.class.getName().lastIndexOf(".") + 1) + typeClass + "Template";
				Class<? extends ItemTemplate> findClass = Class.forName(className).asSubclass(ItemTemplate.class);
				
				ItemTemplate newTemplate = findClass.getDeclaredConstructor(String.class, String.class, String.class, String.class).newInstance(templateName, itemName, itemLongName, itemDesc);
			
				if(!ItemTemplatePool.registerItemTemp(newTemplate)) {
					System.err.println("Duplicate item template " + templateName + " .");
				}
				else {
					System.out.println("Adding item template.");
					
					if(readExtraSection) {
						newTemplate.parseExtraSection(parser);
					}
					
					return newTemplate;
				}			
			} catch (Exception e) {
				System.err.println("Template class is invalid.");
				e.printStackTrace();
			}
		}
		
		if(readExtraSection) {
			while(parser.hasNextLine()) {
				String[] parts = parser.nextLine().split(" ", 2);
				
				if(parts[0].equals("end")) {
					break;
				}
			}
		}
		
		return null;
	}
	
	public static ItemBase parseItemInstInfo(Scanner parser, boolean noRoomNeeded) {
		String itemTemplate = null, roomIdent = null;
		boolean readExtraData = false;
		boolean itemStatic = false;
		int repopTime = 0;
		
		while(parser.hasNextLine()) {
			String[] parts = parser.nextLine().trim().split(" ", 2);
			
			if(parts[0].equals("room")) {
				roomIdent = parts[1].trim();
			}
			else if(parts[0].equals("template")) {
				itemTemplate = parts[1].trim();
			}
			else if(parts[0].equals("static")) {
				itemStatic = true;
			}
			else if(parts[0].equals("repoptime")) {
				repopTime = GameUtils.properIntParser(parts[1]);
			}
			else if(parts[0].equals("extra")) {
				readExtraData = true;
				break;
			}
			else if(parts[0].equals("sub")) {
				parseDummy(parser);
			}
			else if(parts[0].equals("end")) {
				break;
			}
		}
		
		if(itemTemplate != null && (noRoomNeeded || roomIdent != null)) {
			ItemTemplate template = ItemTemplatePool.findByUniqueName(itemTemplate);
			
			if(template != null) {
				GameRoom targetRoom = noRoomNeeded ? null : GameArea.findRoomByIdent(roomIdent);
				boolean useLoader = (!noRoomNeeded && repopTime > 0);
				
				if(noRoomNeeded || targetRoom != null) {
					Class<? extends ItemBase> itemClass = template.getItemClass();
					
					try {
						ItemBase newItem = itemClass.getDeclaredConstructor(ItemTemplate.class, ContainsItems.class).newInstance(template, useLoader ? null : targetRoom);
						if(itemStatic) newItem.setStatic(true);
						
						System.out.println("Adding item instance.");
						
						if(readExtraData) {
							newItem.parseExtraSection(parser);
						}
						
						if(useLoader) {
							ItemLoader loader = new ItemLoader(newItem, repopTime, targetRoom);
							ItemLoaderPool.registerLoader(loader);
							
							newItem = loader.getWorldInstance();
						}
						
						return newItem;
					}
					catch(Exception ex) {
						System.out.println("Item " + itemTemplate + " for room " + roomIdent + " failed to create instance.");
					}
				}
				else {
					System.err.println("Room " + roomIdent + " does not exist for item with template " + itemTemplate + " .");
				}
			}
			else {
				System.err.println("Item template " + itemTemplate + " does not exist - instance was requested.");
			}
		}
		
		return null;
	}
	
	public static NpcTemplate parseNpcTempInfo(Scanner parser) {
		String npcName = null, npcDesc = null, templateName = null;
		int npcMaxEnergy = 0, npcMaxMana = 0, npcMaxHealth = 0, crumbleTime = -1;
		
		while(parser.hasNextLine()) {
			String[] parts = parser.nextLine().trim().split(" ", 2);
			
			if(parts[0].equals("ident")) {
				templateName = parts[1].trim();
			}
			else if(parts[0].equals("name")) {
				npcName = parts[1].trim();
			}
			else if(parts[0].equals("desc")) {
				npcDesc = parts[1].trim();
			}
			else if(parts[0].equals("energy")) {
				npcMaxEnergy = GameUtils.properIntParser(parts[1]);
			}
			else if(parts[0].equals("mana")) {
				npcMaxMana = GameUtils.properIntParser(parts[1]);
			}
			else if(parts[0].equals("health")) {
				npcMaxHealth = GameUtils.properIntParser(parts[1]);
			}
			else if(parts[0].equals("crumbletime")) {
				crumbleTime = Math.max(0, GameUtils.properIntParser(parts[1]));
			}
			else if(parts[0].equals("sub")) {
				parseDummy(parser);
			}
			else if(parts[0].equals("end")) {
				break;
			}
		}
		
		if(npcName != null && npcDesc != null && templateName != null && npcMaxEnergy > 0 && npcMaxMana >= 0 && npcMaxHealth > 0) {
			NpcTemplate newTemplate = new NpcTemplate(templateName, npcMaxEnergy, npcMaxMana, npcMaxHealth, npcName, npcDesc);
			
			if(!NpcTemplatePool.registerTemplate(newTemplate)) {
				System.err.println("Duplicate NPC template " + templateName + " .");
			}
			else {
				System.out.println("Adding NPC template.");
				
				newTemplate.setCrumbleTime(crumbleTime);
				
				return newTemplate;
			}
		}
		
		return null;
	}
	
	public static NpcLoader parseNpcInstInfo(Scanner parser) {
		String npcTemplate = null, roomIdent = null;
		ArrayList<ItemBase> inventory = new ArrayList<ItemBase>();
		int repopTime = 0, moveFreq = 20;
		
		while(parser.hasNextLine()) {
			String[] parts = parser.nextLine().trim().split(" ", 2);
			
			if(parts[0].equals("room")) {
				roomIdent = parts[1].trim();
			}
			else if(parts[0].equals("template")) {
				npcTemplate = parts[1].trim();
			}
			else if(parts[0].equals("repoptime")) {
				repopTime = GameUtils.properIntParser(parts[1].trim());
			}
			else if(parts[0].equals("movefreq")) {
				moveFreq = GameUtils.properIntParser(parts[1].trim());
				if(moveFreq < 0) moveFreq = 0;
			}
			else if(parts[0].equals("sub")) {
				if(parts[1].equals("itemi")) {
					ItemBase newItem = parseItemInstInfo(parser, true);
					if(newItem != null) inventory.add(newItem);
				}
				else {
					parseDummy(parser);
				}
			}
			else if(parts[0].equals("end")) {
				break;
			}
		}
		
		if(npcTemplate != null && roomIdent != null && repopTime >= 0) {
			NpcTemplate template = NpcTemplatePool.findTemplate(npcTemplate);
			
			if(template != null) {
				GameRoom targetRoom = GameArea.findRoomByIdent(roomIdent);
				
				if(targetRoom != null) {
					NpcLoader loader = new NpcLoader(template, targetRoom, repopTime, inventory);
					loader.setMoveFreq(moveFreq);
					NpcLoaderPool.registerLoader(loader);
					
					System.out.println("Adding NPC instance loader.");
					
					return loader;
				}
				else {
					System.err.println("Room " + roomIdent + " does not exist for NPC with template " + npcTemplate + " .");
				}
			}
			else {
				System.err.println("NPC template " + npcTemplate + " does not exist - instance was requested.");
			}
		}
		
		return null;
	}
	
	public static void parseDummy(Scanner parser) {
		while(parser.hasNextLine()) {
			String[] parts = parser.nextLine().trim().split(" ", 2);
			
			if(parts[0].equals("sub")) {
				parseDummy(parser);
			}
			else if(parts[0].equals("end")) {
				break;
			}
		}
	}
	
	private DataParser() {
		
	}
}
